/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2002-2007 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"
#if defined (UMC_ENABLE_MP3_INT_AUDIO_ENCODER)

#include "mp3enc_own_int.h"

/* Coefficient Wi * (2/3)^2 * sqrt(2) in Q31 */
static const Ipp32s pIMWin[4][36] =
{
    {
        58876489, 176181383, 292145429, 405886073, 516537678,
        623258120, 725235191, 821692784, 911896799, 995160729,
        1070850885, 1138391219, 1197267708, 1247032268, 1287306158,
        1317782872, 1338230462, 1348493310, 1348493310, 1338230462,
        1317782872, 1287306158, 1247032268, 1197267708, 1138391219,
        1070850885, 995160729, 911896799, 821692784, 725235191,
        623258120, 516537678, 405886073, 292145429, 176181383, 58876489
    },

    {
        58876489, 176181383, 292145429, 405886073, 516537678, 623258120,
        725235191, 821692784, 911896799, 995160729, 1070850885, 1138391219,
        1197267708, 1247032268, 1287306158, 1317782872, 1338230462, 1348493310,
        1349778000, 1349778000, 1349778000, 1349778000, 1349778000, 1349778000,
        1338230462, 1247032268, 1070850885, 821692784, 516537678, 176181383,
        0,0,0,0,0,0
    },

    {
        176181383, 516537678, 821692784, 1070850885, 1247032268, 1338230462,
        1338230462, 1247032268, 1070850885, 821692784, 516537678, 176181383,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    },

    {
        0,0,0,0,0,0,
        176181383,516537678,821692784,1070850885,1247032268,1338230462,
        1349778000,1349778000,1349778000,1349778000,1349778000,1349778000,
        1348493310,1338230462,1317782872,1287306158,1247032268,1197267708,
        1138391219,1070850885,995160729,911896799,821692784,725235191,623258120,
        516537678,405886073,292145429,176181383,58876489
    }
};


/*
//  cos(17*pi/72)  cos(15*pi/72)   cos(13*pi/72)   cos(11*pi/72)
//  cos( 9*pi/72)  cos( 7*pi/72)   cos( 5*pi/72)   cos( 3*pi/72)
//  cos(   pi/72)
//  in Q31
*/

static const Ipp32s _d72cos[9] =
{
    1583291025, 1703713325, 1811169339, 1904841260,
    1984016189, 2048091557, 2096579711, 2129111628,
    2145439719
};

/*
//  sin(17*pi/72)  sin(15*pi/72)   sin(13*pi/72)   sin(11*pi/72)
//  sin( 9*pi/72)  sin( 7*pi/72)   sin( 5*pi/72)   sin( 3*pi/72)
//  sin(   pi/72)
//  in Q31
*/

static const Ipp32s _d72sin[9] =
{
    1450818924, 1307305214, 1153842123, 991597596,
    821806413, 645760787, 464800532, 280302863,
    93671921
};


/* coefficient in Q31 */
static const Ipp32s _dt[8] =
{
    1859775393, 1073741824, -2017974537, 372906622,
    1645067915, 734482665, 2114858546, 1380375881
};


/*
//  cos(pi/24) cos(3*pi/24) cos(5*pi/24)
//  sin(pi/24) sin(3*pi/24) sin(5*pi/24)
//  in Q31
*/

static const Ipp32s _dcoef12[6] =
{
    2129111628, 1984016189, 1703713325, 280302863, 821806413, 1307305214
};

/*/////////////////////////////////////////////////////////////////////////////
// Name:             ownsMDCT_36_32s
// Description:      Performs 36 points windowing and Forward MDCT for long block.
// Input Arguments : pSrc           - pointer to the input buffer which is the
//                                    output of windowing.
//                   blockType      - indication of the window type:
//                                  - 0: normal window; 1: start block;
//                                  - 2: Ipp16s block;   3: stop block.
// Output Arguments: pDst           - pointer to the output buffer of Forward MDCT
//                                    for long block.
******************************************************************************/

static void ownsMDCT_36_32s(Ipp32s* pSrc,
                            Ipp32s* pDst,
                            Ipp32s  blockType)
{
  Ipp32s n;
  Ipp32s y[18], x[18], xa[9], a[27], m[11];

  {
    Ipp32s data[36], data2[36];

    /* phase 1 */
    for( n = 0; n < 36; n += 4 ) {
        data[n+0] = _IPPS_MUL32_MP3_32S(pSrc[n+0], pIMWin[blockType][n+0]);
        data[n+1] = _IPPS_MUL32_MP3_32S(pSrc[n+1], pIMWin[blockType][n+1]);
        data[n+2] = _IPPS_MUL32_MP3_32S(pSrc[n+2], pIMWin[blockType][n+2]);
        data[n+3] = _IPPS_MUL32_MP3_32S(pSrc[n+3], pIMWin[blockType][n+3]);
    }

    /* phase 2 */
    for( n = 0; n < 18; n += 2 )
    {
        data2[n]    = data[n]   + data[35-n];
        data2[35-n] = data[n]   - data[35-n];
        data2[n+1]  = data[n+1] + data[34-n];
        data2[34-n] = data[n+1] - data[34-n];
    }

    /* phase 3 */
    for( n = 0; n < 9; n++ )
    {
        y[n]   = data2[27+n] - data2[9+n];
        y[9+n] = data2[n]    - data2[18+n];
    }

    /* phase 4 */
    for( n = 0; n < 9; n++ )
    {
        x[n]   = (Ipp32s)(((Ipp64s)y[n]    * _d72cos[n] -
                           (Ipp64s)y[17-n] * _d72sin[n]) >> 31);
        x[9+n] = (Ipp32s)(((Ipp64s)y[9+n]  * _d72cos[8-n] +
                           (Ipp64s)y[8-n]  * _d72sin[8-n])>>31);
    }
  }

  /* phase 5: 9 points DCT-II & DST-II */
  /* Reverse Order of Input of DCT-II */
  for( n = 0; n < 9; n++ )
  {
    xa[n] = x[8-n];
  }

  /* 9 points DCT-II */
  a[1] = xa[3] + xa[5];
  a[2] = xa[3] - xa[5];
  a[3] = xa[6] + xa[2];
  a[4] = xa[6] - xa[2];
  a[5] = xa[1] + xa[7];
  a[6] = xa[1] - xa[7];
  a[7] = xa[8] + xa[0];
  a[8] = xa[8] - xa[0];

  a[9] = xa[4] + a[5];
  a[10] = a[1] + a[3];
  a[11] = a[10] + a[7];
  a[12] = a[3] - a[7];
  a[13] = a[1] - a[7];
  a[14] = a[1] - a[3];
  a[15] = a[2] - a[4];
  a[16] = a[15] + a[8];
  a[17] = a[4] + a[8];
  a[18] = a[2] - a[8];
  a[19] = a[2] + a[4];

  m[1]  = (Ipp32s)(((Ipp64s) - _dt[0] * a[6])>>31);
  m[2]  = (Ipp32s)(((Ipp64s)   _dt[1] * a[5])>>31);
  m[3]  = (Ipp32s)(((Ipp64s)   _dt[1] * a[11])>>31);
  m[4]  = (Ipp32s)(((Ipp64s) - _dt[2] * a[12])>>31);
  m[5]  = (Ipp32s)(((Ipp64s) - _dt[3] * a[13])>>31);
  m[6]  = (Ipp32s)(((Ipp64s) - _dt[4] * a[14])>>31);
  m[7]  = (Ipp32s)(((Ipp64s) - _dt[0] * a[16])>>31);
  m[8]  = (Ipp32s)(((Ipp64s) - _dt[5] * a[17])>>31);
  m[9]  = (Ipp32s)(((Ipp64s) - _dt[6] * a[18])>>31);
  m[10] = (Ipp32s)(((Ipp64s) - _dt[7] * a[19])>>31);

  a[20] = xa[4] - m[2];
  a[21] = a[20] + m[4];
  a[22] = a[20] - m[4];
  a[23] = a[20] + m[5];
  a[24] = m[1] + m[8];
  a[25] = m[1] - m[8];
  a[26] = m[1] + m[9];

  y[0] = a[9] + a[11];
  y[1] = m[10] - a[26];
  y[2] = m[6] - a[21];
  y[3] = m[7];
  y[4] = a[22] - m[5];
  y[5] = a[25] - m[9];
  y[6] = m[3] - a[9];
  y[7] = a[24] + m[10];
  y[8] = a[23] + m[6];

  /* Keep Order of Input of DST-II */
  for( n = 0; n < 9; n++ )
  {
    xa[n] = x[9+n];
  }

  xa[1] = -xa[1];
  xa[3] = -xa[3];
  xa[5] = -xa[5];
  xa[7] = -xa[7];

  /* 9 points DCT-II */
  a[1] = xa[3] + xa[5];
  a[2] = xa[3] - xa[5];
  a[3] = xa[6] + xa[2];
  a[4] = xa[6] - xa[2];
  a[5] = xa[1] + xa[7];
  a[6] = xa[1] - xa[7];
  a[7] = xa[8] + xa[0];
  a[8] = xa[8] - xa[0];

  a[9] = xa[4] + a[5];
  a[10] = a[1] + a[3];
  a[11] = a[10] + a[7];
  a[12] = a[3] - a[7];
  a[13] = a[1] - a[7];
  a[14] = a[1] - a[3];
  a[15] = a[2] - a[4];
  a[16] = a[15] + a[8];
  a[17] = a[4] + a[8];
  a[18] = a[2] - a[8];
  a[19] = a[2] + a[4];

  m[1] = (Ipp32s)(((Ipp64s)-_dt[0] * a[6])>>31);
  m[2] = (Ipp32s)(((Ipp64s)_dt[1] * a[5])>>31);
  m[3] = (Ipp32s)(((Ipp64s)_dt[1] * a[11])>>31);
  m[4] = (Ipp32s)(((Ipp64s)-_dt[2] * a[12])>>31);
  m[5] = (Ipp32s)(((Ipp64s)-_dt[3] * a[13])>>31);
  m[6] = (Ipp32s)(((Ipp64s)-_dt[4] * a[14])>>31);
  m[7] = (Ipp32s)(((Ipp64s)-_dt[0] * a[16])>>31);
  m[8] = (Ipp32s)(((Ipp64s)-_dt[5] * a[17])>>31);
  m[9] = (Ipp32s)(((Ipp64s)-_dt[6] * a[18])>>31);
  m[10] = (Ipp32s)(((Ipp64s)-_dt[7] * a[19])>>31);

  a[20] = xa[4] - m[2];
  a[21] = a[20] + m[4];
  a[22] = a[20] - m[4];
  a[23] = a[20] + m[5];
  a[24] = m[1] + m[8];
  a[25] = m[1] - m[8];
  a[26] = m[1] + m[9];

  /* Reverse output order of DST-II */
  y[9] = a[9] + a[11];
  y[10] = m[10] - a[26];
  y[11] = m[6] - a[21];
  y[12] = m[7];
  y[13] = a[22] - m[5];
  y[14] = a[25] - m[9];
  y[15] = m[3] - a[9];
  y[16] = a[24] + m[10];
  y[17] = a[23] + m[6];

  /* phase 6: output */
  pDst[0] = y[0];
  pDst[1] = -y[1] - y[17];
  pDst[2] = -y[1] + y[17];
  pDst[3] = y[2] + y[16];
  pDst[4] = y[2] - y[16];
  pDst[5] = -y[3] - y[15];
  pDst[6] = -y[3] + y[15];
  pDst[7] = y[4] + y[14];
  pDst[8] = y[4] - y[14];
  pDst[9] = -y[5] - y[13];
  pDst[10] = -y[5] + y[13];
  pDst[11] = y[6] + y[12];
  pDst[12] = y[6] - y[12];
  pDst[13] = -y[7] - y[11];
  pDst[14] = -y[7] + y[11];
  pDst[15] = y[8] + y[10];
  pDst[16] = y[8] - y[10];
  pDst[17] = -y[9];

  return;
} /* ownsMDCT_36_32s() */

static void ownsMDCT_12x3_32s(Ipp32s* pSrc, Ipp32s* pDst)
{
    {
    Ipp32s w;
    Ipp32s data[12], data2[12], y[6], x[6], a1, a2, m1, m2;

    for( w = 0; w < 3; w ++ ) {
        /* phase 1 */
        data[ 0] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 0]*3, pIMWin[2][ 0] );
        data[ 1] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 1]*3, pIMWin[2][ 1] );
        data[ 2] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 2]*3, pIMWin[2][ 2] );
        data[ 3] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 3]*3, pIMWin[2][ 3] );
        data[ 4] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 4]*3, pIMWin[2][ 4] );
        data[ 5] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 5]*3, pIMWin[2][ 5] );
        data[ 6] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 6]*3, pIMWin[2][ 6] );
        data[ 7] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 7]*3, pIMWin[2][ 7] );
        data[ 8] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 8]*3, pIMWin[2][ 8] );
        data[ 9] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+ 9]*3, pIMWin[2][ 9] );
        data[10] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+10]*3, pIMWin[2][10] );
        data[11] = _IPPS_MUL32_MP3_32S( pSrc[6+w*6+11]*3, pIMWin[2][11] );

        data2[ 0] = data[0]+data[11];
        data2[ 1] = data[1]+data[10];
        data2[ 2] = data[2]+data[ 9];
        data2[ 3] = data[3]+data[ 8];
        data2[ 4] = data[4]+data[ 7];
        data2[ 5] = data[5]+data[ 6];
        data2[ 6] = data[5]-data[ 6];
        data2[ 7] = data[4]-data[ 7];
        data2[ 8] = data[3]-data[ 8];
        data2[ 9] = data[2]-data[ 9];
        data2[10] = data[1]-data[10];
        data2[11] = data[0]-data[11];

        /* phase 2 */
        y[0] = data2[ 9] - data2[3];
        y[1] = data2[10] - data2[4];
        y[2] = data2[11] - data2[5];
        y[3] = data2[ 0] - data2[6];
        y[4] = data2[ 1] - data2[7];
        y[5] = data2[ 2] - data2[8];

        /* phase 3 */
        x[0] = (Ipp32s)(((Ipp64s)y[0] * _dcoef12[2] - (Ipp64s)y[5] * _dcoef12[5])>>31);
        x[1] = (Ipp32s)(((Ipp64s)y[1] * _dcoef12[1] - (Ipp64s)y[4] * _dcoef12[4])>>31);
        x[2] = (Ipp32s)(((Ipp64s)y[2] * _dcoef12[0] - (Ipp64s)y[3] * _dcoef12[3])>>31);
        x[3] = (Ipp32s)(((Ipp64s)y[3] * _dcoef12[0] + (Ipp64s)y[2] * _dcoef12[3])>>31);
        x[4] = (Ipp32s)(((Ipp64s)y[4] * _dcoef12[1] + (Ipp64s)y[1] * _dcoef12[4])>>31);
        x[5] = (Ipp32s)(((Ipp64s)y[5] * _dcoef12[2] + (Ipp64s)y[0] * _dcoef12[5])>>31);

        /* phase 4: 3 points DCT II & DST II */
        /* Reverse Input Order & DCT II */
        a1 = x[2]+x[0];
        a2 = x[2]-x[0];
        m1 = (Ipp32s)(((Ipp64s)_dt[0] * a2)>>31);
        m2 = (Ipp32s)(((Ipp64s)_dt[1] * a1)>>31);
        y[0] = (x[1]+a1);
        y[1] = m1;
        y[2] = m2-x[1];

        /* DST II */
        a1 = x[3] + x[5];
        a2 = x[3] - x[5];
        m1 = (Ipp32s)(((Ipp64s)_dt[0]*a2)>>31);
        m2 = (Ipp32s)(((Ipp64s)_dt[1]*a1)>>31);
        /* Reverse Output Order of DST */
        y[5] = m2 + x[4];
        y[4] = m1;
        y[3] = (a1 - x[4]);

        /* phase 5 */
        /* pDst[k+w*6] = tmp * (4.0/N); */
        x[0] = y[0];
        x[1] = -y[1]+y[5];
        x[2] = y[2]-y[4];
        x[3] = y[3];
        x[4] = -y[4]-y[2];
        x[5] = y[5]+y[1];

        pDst[w*6+0] = x[0];
        pDst[w*6+1] = - x[5];
        pDst[w*6+2] = x[1];
        pDst[w*6+3] = - x[4];
        pDst[w*6+4] = x[2];
        pDst[w*6+5] = - x[3];
    }
    }

  return;
} /* ownsMDCT_12x3_32s() */

/******************************************************************************
//  Name:
//    mdct_init
//
//  Description:
//    Initialize coefficients of mdct windows
//
//  Input Arguments:
//     - pointer to encoder context
//
//  Output Arguments:
//
//  Returns:
//    -
//
******************************************************************************/

void mp3ienc_mdctInit(MP3Enc *state)
{
  if (state)
    ippsZero_32s(&state->fbout_data[0][0][0][0],2*3*18*32);
}

/******************************************************************************
//  Name:
//    mdct
//
//  Description:
//    Perform windowing and appling of mdct.
//
//  Input Arguments:
//             - pointer to encoder context
//    in         - input samples
//   block_type  - type of block
//
//  Output Arguments:
//    out        - samples in frequency domain
//
//  Returns:
//    1 - all ok
******************************************************************************/

Ipp32s mp3ienc_mdctInSubband(MP3Enc *state,
                             Ipp32s *in,
                             Ipp32s *out,
                             Ipp32s block_type)
{
    Ipp32s i, j;
    Ipp32s *tout = out;

    if (block_type == 2) {
        Ipp32s   tmp_out[36];
        Ipp32s  *tmp_out2[3];

        tmp_out2[0] = tmp_out;
        tmp_out2[1] = tmp_out + 6;
        tmp_out2[2] = tmp_out + 12;

        for (i = 0; i < state->com.lowpass_maxline; i++) {
            ownsMDCT_12x3_32s(in, tmp_out);
            for(j = 0; j < 6; j++) {
                out[3 * j + 0] = tmp_out2[0][j];
                out[3 * j + 1] = tmp_out2[1][j];
                out[3 * j + 2] = tmp_out2[2][j];
            }

            in += 36;
            out += 18;
        }
    } else {
        for (i = 0; i < state->com.lowpass_maxline; i++) {

            ownsMDCT_36_32s(in, out, block_type);
            in += 36;
            out += 18;
        }
    }

    if (state->com.lowpass_maxline < 32) {
        ippsZero_32s(tout + state->com.lowpass_maxline * 18, 18 * (32 - state->com.lowpass_maxline));
    }

    return 1;
}

/******************************************************************************
//  Name:
//    mdctBlock
//
//  Description:
//    The output of the filterbank is the input to the subdivision using the MDCT.
//    18 consecutive output values of one granule and 18 output values of the granule before
//    are assembled to one block of 36 samples for each subbands.
//
//  Input Arguments:
//             - pointer to encoder context
//
//  Output Arguments:
//    -
//
//  Returns:
//    1 - all ok.
******************************************************************************/

/* Q31 */
static Ipp32s aa_cs[8] = {
    1841452032,
    1893526528,
    2039312000,
    2111651968,
    2137858176,
    2145681024,
    2147267200,
    2147468928,
};

/* Q31 */
static Ipp32s aa_ca[8] = {
    -1104871168,
    -1013036672,
    -672972928,
    -390655616,
    -203096528,
    -87972920,
    -30491194,
    -7945636,
};

Ipp32s mp3ienc_mdctBlock(MP3Enc *state)
{
    VM_ALIGN16_DECL(Ipp32s) in[1152];
    Ipp32s  ch, gr, bnd, k, j;
    Ipp32s  bu, bd;
    Ipp32s  *ptr_fbout;

    for (gr = 1; gr <= 2; gr++) {
        for (ch = 0; ch < state->com.stereo; ch++) {
            ptr_fbout = &((*(state->fbout_int[gr]))[ch][0][0]);
            for (k = 33; k < 576; k += 32)
                for (j = 0; j < 32; j += 2, k += 2)
                    ptr_fbout[k] = -ptr_fbout[k];
        }
    }

    for (gr = 0; gr < 2; gr++) {
        for (ch = 0; ch < state->com.stereo; ch++) {
            for (bnd = 0, j = 0; bnd < 32; bnd++) {
                for (k = 0; k < 18; k++) {
                    in[j + k] = (*(state->fbout_int[gr]))[ch][k][bnd];
                    in[j + k + 18] = (*(state->fbout_int[gr + 1]))[ch][k][bnd];
                }
                j += 36;
            }

            mp3ienc_mdctInSubband(state, in, state->mdct_out_int[gr][ch], state->com.si_blockType[gr][ch]);
        }
        for (ch = 0; ch < state->com.stereo; ch++) {
            if (state->com.si_blockType[gr][ch] != 2) {
                Ipp32s  idx1, idx2;

                j = 0;
                for (bnd = 0; bnd < 31; bnd++) {
                    for (k = 0; k < 8; k++) {
                        idx1 = j + 17 - k;
                        idx2 = j + 18 + k;

                        bu = state->mdct_out_int[gr][ch][idx1] << 1;
                        bd = state->mdct_out_int[gr][ch][idx2] << 1;
                        state->mdct_out_int[gr][ch][idx1] =
                            MUL32_MP3_32S(bu, aa_cs[k]) +
                            MUL32_MP3_32S(bd, aa_ca[k]);
                        state->mdct_out_int[gr][ch][idx2] =
                            MUL32_MP3_32S(bd, aa_cs[k]) -
                            MUL32_MP3_32S(bu, aa_ca[k]);
                    }
                    j += 18;
                }
            } else {
              Ipp32s i, sfb, start, end, line, window;
              Ipp32u *sfb_short = mp3enc_sfBandIndex[state->com.header.id][state->com.header.samplingFreq].s;
              Ipp32s *mdct_out = state->mdct_out_int[gr][ch];

              i = 0;
              for (sfb = 0; sfb < 13; sfb++) {
                start = sfb_short[sfb];
                end = sfb_short[sfb + 1];

                for (window = 0; window < 3; window++) {
                  for (line = start; line < end; line += 2) {
                    in[i++] = mdct_out[line*3  +window];
                    in[i++] = mdct_out[line*3+3+window];
                  }
                }
              }
              ippsCopy_32s(in, mdct_out, 576);
            }
        }
        if (state->com.stereo_mode == MPA_MS_STEREO) {
          const Ipp32s mult_i = 1518500249;
          Ipp32s *ptrL = &(state->mdct_out_int[gr][0][0]);
          Ipp32s *ptrR = &(state->mdct_out_int[gr][1][0]);
          Ipp32s li, ri;
          for (k = 0; k < 576; k++) {
            li = MUL32_MP3_32S(mult_i, (ptrL[0] + ptrR[0]) << 1);
            ri = MUL32_MP3_32S(mult_i, (ptrL[0] - ptrR[0]) << 1);
            ptrL[0] = li;
            ptrR[0] = ri;
            ptrL++; ptrR++;
          }
        }
    }

    state->fbout_prev += 2;
    if (state->fbout_prev > 2)
      state->fbout_prev -= 3;

    for (j = 0; j < 3; j++) {
      Ipp32s ind;
      ind = state->fbout_prev + j;
      if (ind > 2) ind -= 3;
      state->fbout_int[j] = &state->fbout_data[ind];
    }

    return 1;
}

#endif //UMC_ENABLE_MP3_INT_AUDIO_ENCODER
